package com.lhf.fvsooa.config;

import com.lhf.common.enums.Status;
import com.lhf.fvsooa.domain.User;
import com.lhf.fvsooa.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.error.DefaultWebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.token.*;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import javax.annotation.Resource;
import java.security.KeyPair;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p></p>
 *
 * @author liuhf
 * @createTime 2021/4/1 10:19
 * @since 1.0
 */
@Configuration
@EnableAuthorizationServer
public class AuthServerConfig implements AuthorizationServerConfigurer {
    @Resource(name = "passwordEncoder")
    private PasswordEncoder passwordEncoder;
    @Resource(name = "authenticationManager")
    private AuthenticationManager authenticationManager;
    @Resource(name = "userDetailsService")
    private UserDetailsServiceImpl userDetailsService;

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("fvs")
                .secret(passwordEncoder.encode("123456"))
                .scopes("all")
                .resourceIds("store")
                .autoApprove(true)
                .authorizedGrantTypes("authorization_code", "refresh_token", "password")
                .redirectUris("https://oauth.pstmn.io/v1/callback", "https://baidu.com");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

        endpoints.authenticationManager(this.authenticationManager)
                .accessTokenConverter(this.jwtAccessTokenConverter())
                .tokenServices(this.tokenServices())
                .userDetailsService(userDetailsService)
                .exceptionTranslator(new WebResponseExceptionTranslatorOwn())
                .reuseRefreshTokens(false);
        ;
    }

    @Bean
    public AuthorizationServerTokenServices tokenServices() {

        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> tokenEnhancers = Arrays.asList(this.tokenEnhancer(), this.jwtAccessTokenConverter());
        tokenEnhancerChain.setTokenEnhancers(tokenEnhancers);

        DefaultTokenServices services = new DefaultTokenServices();
        services.setTokenStore(this.tokenStore());
        services.setTokenEnhancer(tokenEnhancerChain);
        services.setAccessTokenValiditySeconds(5000);
        services.setRefreshTokenValiditySeconds(5000);
        services.setSupportRefreshToken(true);
        return services;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(this.jwtAccessTokenConverter());
    }

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//        converter.setSigningKey("public");
        converter.setKeyPair(this.keyPair());
        return converter;
    }

    //
//    keytool -genkey -alias youlai -keyalg RSA -keypass 123456 -keystore youlai.jks -storepass 123456
    @Bean
    public KeyPair keyPair() {
        KeyStoreKeyFactory factory = new KeyStoreKeyFactory(
                new ClassPathResource("lhf.jks"), "123456".toCharArray());
        return factory.getKeyPair(
                "lhf", "123456".toCharArray());
    }

    @Bean
    public TokenEnhancer tokenEnhancer() {
        return (accessToken, authentication) -> {
            User user = (User) authentication.getUserAuthentication().getPrincipal();
            Map<String, Object> map = new HashMap<>(8);
            map.put("user_id", user.getUid());
            map.put("user_name", user.getUsername());
            map.put("phone", user.getPhone());
            map.put("email", user.getEmail());
            map.put("head_img", user.getHeadImg());
            ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(map);
            return accessToken;
        };
    }

    /**
     * 自定义异常返回
     */
    private static class WebResponseExceptionTranslatorOwn implements WebResponseExceptionTranslator<OAuth2Exception> {


        @Override
        public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {
            e.printStackTrace();
            ResponseEntity<OAuth2Exception> result = null;
            if (e instanceof InvalidGrantException) {
                InvalidGrantException exception = (InvalidGrantException) e;
                if (exception.getOAuth2ErrorCode().equals(OAuth2Exception.INVALID_GRANT)) {
                    result = new ResponseEntity<>(OAuth2Exception.create(String.valueOf(Status.BAD_CREDENTIALS.code), Status.BAD_CREDENTIALS.mes), HttpStatus.valueOf(401));
                } else {
                    result = new DefaultWebResponseExceptionTranslator().translate(e);
                }
            } else {
                result = new DefaultWebResponseExceptionTranslator().translate(e);
            }
            return result;
        }


    }
}
